use std::{borrow::Cow, sync::LazyLock, time::Duration};

use jsonwebtoken::{
  Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode,
  get_current_timestamp,
};
use serde::{Deserialize, Serialize};

const DEFAULT_SECRET: &str = "0123456789";
static DEFAULT_JWT: LazyLock<JWT> = LazyLock::new(|| JWT::default());

#[derive(Debug, Clone, Serialize)]
pub struct Principal {
  pub id: String,
  pub name: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
  jti: String,
  sub: String,
  aud: String,
  iss: String,
  iat: u64,
  exp: u64,
}

#[derive(Debug)]
pub struct JwtConfig {
  pub secret: Cow<'static, str>,
  pub expireation: Duration,
  pub audience: String,
  pub issuser: String,
}

impl Default for JwtConfig {
  fn default() -> Self {
    Self {
      secret: Cow::Borrowed(DEFAULT_SECRET), // 字符串的拷贝,性能不错
      expireation: Duration::from_secs(60 * 60),
      audience: "audience".to_string(),
      issuser: "issuser".to_string(),
    }
  }
}

#[derive(Clone)]
pub struct JWT {
  // secret: EncodingKey,
  encode_secret: EncodingKey,
  decode_secret: DecodingKey,
  header: Header,
  validation: Validation,
  expriation: Duration,
  audience: String,
  issuser: String,
}

impl JWT {
  pub fn new(config: JwtConfig) -> Self {
    let mut validation = Validation::new(Algorithm::HS256);
    validation.set_audience(&[&config.audience]);
    validation.set_issuer(&[&config.issuser]);
    // 那一些必要的
    validation
      .set_required_spec_claims(&["jti", "sub", "aud", "iss", "iat", "exp"]);

    let secret = config.secret.as_bytes();
    Self {
      validation,
      encode_secret: EncodingKey::from_secret(secret),
      decode_secret: DecodingKey::from_secret(secret),
      header: Header::new(Algorithm::HS256),
      expriation: config.expireation,
      audience: config.audience,
      issuser: config.issuser,
    }
  }

  pub fn encode(&self, principal: Principal) -> anyhow::Result<String> {
    let current_timestamp = get_current_timestamp();

    let claims = Claims {
      jti: xid::new().to_string(),
      // 可以是很多信息
      sub: format!("{}:{}", principal.id, principal.name),
      aud: self.audience.clone(),
      iss: self.issuser.clone(),
      iat: current_timestamp,
      exp: current_timestamp.saturating_add(self.expriation.as_secs()),
    };

    Ok(encode(&self.header, &claims, &self.encode_secret)?)
  }

  pub fn decode(&self, token: &str) -> anyhow::Result<Principal> {
    let claims: Claims =
      decode(token, &self.decode_secret, &self.validation)?.claims;
    let mut parts = claims.sub.splitn(2, ':');
    let principal = Principal {
      id: parts.next().unwrap().to_string(),
      name: parts.next().unwrap().to_string(),
    };

    Ok(principal)
  }
}

impl Default for JWT {
  fn default() -> Self {
    Self::new(JwtConfig::default())
  }
}

pub fn get_jwt() -> &'static JWT {
  &DEFAULT_JWT
}

// curl http://127.0.0.1:3000/api/users\?page\=1\&size\=10\&keyword\=xx -X GET -H "Authorization: Bearer xxxx" -H "Content-Type: application/json"
